#ifndef TRACE_SMART_HPP
#define TRACE_SMART_HPP

/**
 * \file
 * \ingroup trace
 * \brief cas::TraceSmart declaration.
 */

#include "core/global-value.hpp"
#include "trace/trace-gen.hpp"
#include "trace/trace-driver.hpp"
#include "utils/convert.hpp"

namespace cas
{
    /** \ingroup trace */
    static constexpr bool SmartRead = false;
    static constexpr bool SmartWrite = true;

    /** \ingroup trace */
    struct SmartIOData
    {
        uint64_t lba;
        uint32_t size;
        bool rw; /**< read=false, write=true */

        SmartIOData(uint64_t _lba = 0, uint32_t _size = 0, bool _rw = false)
            : lba(_lba), size(_size), rw(_rw){};
    } __attribute__((packed));

    /** \ingroup trace */
    struct SmartIOMetadata : public SmartIOData
    {
        enum Op : uint32_t
        {
            SmartIOOpen,
            SmartIOClose,
            SmartIOStat,
            SmartIOOpNum
        };

        SmartIOMetadata(uint64_t inode = 0, Op type = SmartIOOpen)
        {
            lba = inode;
            size = (uint32_t)type;
        };

        inline uint64_t getInode() const { return lba; }
        inline Op getOp() const { return (Op)size; }
    };

    typedef std::shared_ptr<BaseTask<SmartIOData>> SmartBaseTaskPtr;

    /** \ingroup trace */
    class BlockTask : public BaseTask<SmartIOData>
    {
    public:
        BlockTask();
        BlockTask(uint64_t offset, uint64_t size, uint32_t bs = 4096, bool rw = SmartRead);
        ~BlockTask() = default;

        inline SmartIOData next() override;
        /** remained steps */
        inline uint32_t remains() const;

        typedef std::shared_ptr<BlockTask> SharedPtr;
        static SharedPtr makeFromToml(const toml::value &config, const std::string &logPrefix);

        inline std::string typeName() const override { return "BlockTask"; }

    private:
        uint64_t offset; /**< starting address */
        uint64_t size;   /**< total access size */
        uint32_t bs;     /**< block size */
        bool rw;         /**< read=false, write=true */

        uint64_t curr; /**< current offset */
    };

    /** \ingroup trace */
    class RandomTask : public BaseTask<SmartIOData>
    {
    public:
        RandomTask();
        RandomTask(uint64_t offset, uint64_t size, uint32_t num = 2, uint32_t repeat = 1, uint32_t bs = 4096, bool rw = SmartRead);
        ~RandomTask() = default;

        inline SmartIOData next() override;

        typedef std::shared_ptr<RandomTask> SharedPtr;
        typedef std::shared_ptr<Int64RND> RndPtr;
        static SharedPtr makeFromToml(const toml::value &config, const std::string &logPrefix);

        inline std::string typeName() const override { return "RandomTask"; }

    private:
        uint64_t offset; /**< starting address */
        uint64_t size;   /**< access size of each section */
        uint32_t num;    /**< section jumping times */
        uint32_t repeat; /**< repeat times of each section */
        uint32_t bs;     /**< block size */
        bool rw;         /**< read=false, write=true */
        RndPtr rnd;

        uint32_t currSection;    // index of sections
        uint32_t currRepeat;     // index of repeat
        uint64_t currBaseOffset; // offset of a section
        uint64_t currOffset;     // current offset

        void reset();
        void jumpRandom();
    };

    class SequenceTask : public BaseTask<SmartIOData>
    {
    public:
        inline std::string typeName() const override { return "SequenceTask"; }

    private:
        uint64_t offset; /**< starting address */
        uint64_t size;   /**< access size of each section */
        uint32_t num;    /**< number of sections */
        int64_t step;    /**< step of adjacent sections */
        uint32_t repeat; /**< repeat times of each section */
        uint32_t bs;     /**< block size */
        bool rw;         /**< read=false, write=true */
    };

    class TraceTask : public BaseTask<SmartIOData>
    {
    public:
        inline std::string typeName() const override { return "TraceTask"; }

    private:
        std::string path;          /**< trace file path */
        uint64_t offset = 0;       /**< trace address offset */
        bool useTimestamp = false; /**< use timestamp for interweave decision */
        std::shared_ptr<TraceDriver> driver;
    };

    /** \ingroup trace */
    class MixTask : public BaseTask<SmartIOData>
    {
    public:
        enum MixPolicy
        {
            MixSeries,     /**< series tasks */
            MixInterweave, /**< interweave tasks with different steps */
            MixRandom,     /**< randomly mixture tasks */
            MixPoilcyNum
        };

        typedef SmartIOData (MixTask::*SmartTaskNext)();
        typedef std::shared_ptr<Int64RND> RndPtr;

        MixTask(MixPolicy policy = MixSeries);
        MixTask(const char *policyName);
        ~MixTask();

        inline SmartIOData next() override;
        /** Push back a task */
        void pushBack(const SmartBaseTaskPtr task);
        /** Call after all \c pushBack calls */
        void pushEnd();

        typedef std::shared_ptr<MixTask> SharedPtr;
        static SharedPtr makeFromToml(const toml::value &config, const std::string &logPrefix);

        inline std::string typeName() const override { return "MixTask"; }
        friend class TraceSmart;

    private:
        MixPolicy policy;
        std::vector<SmartBaseTaskPtr> tasks;

        std::vector<uint32_t> steps; /**< MixInterweave steps */
        bool timestampStep;          /**< MixInterweave use timestamp */
        std::vector<double> dist;    /**< MixRandom probability */
        RndPtr rnd;                  /**< MixRandom random number generator */

        size_t currTaskIdx;
        size_t currTaskSteps;
        SmartBaseTaskPtr currTask;
        SmartTaskNext nextMixFunc;

        inline SmartIOData nextMixSeries();
        inline SmartIOData nextMixInterweave();
        inline SmartIOData nextMixRandom();
        inline SmartIOData nextMixTrace();

        void initPolicy();
    };

    /** \ingroup trace */
    class TraceSmart : public TraceGenerator
    {
    public:
        TraceSmart();
        TraceSmart(const std::string &configFile);
        ~TraceSmart() override;

        inline SmartIOData next();
        inline bool isEnd() const { return rootTask.isEnd(); }
        inline size_t getCurrentTaskIndex() { return rootTask.currTaskIdx; }

        void dump();
        void dumpSteps(const uint64_t maxSteps);

        inline std::string typeName() const override { return "TraceSmart"; }

    private:
        MixTask rootTask; /**< MixTask::MixSeries */
        uint64_t seed;    /**< random seed */

        void parseConfig(const toml::value &config) override;
    };
}

#endif /* TRACE_SMART_HPP */